using System.Text;
using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Packets;
using MQTTnet.Protocol;

namespace MQTTDebugTool.Utils;

public class MQTTClientManager
{
    
    #region Buliding
    private static Dictionary<string, Func<byte[], Task>> TopicHandlers { get; } = new();

    public static async Task<MQTTClientManager?> ConnectionAsync(
        MQTTClientConfig? clientOptions = null,
        Func<MqttClientConnectedEventArgs, Task>? connectedEvent = null,
        Func<MqttClientDisconnectedEventArgs, Task>? disconnectedEvent = null)
    {
        clientOptions ??= new MQTTClientConfig();

        // Create MQTT Client
        var mqttClient = new MqttFactory().CreateMqttClient();

        // MQTT Connected Events
        mqttClient.ConnectedAsync += (e) =>
        {
            Console.WriteLine("Connected to MQTT Broker");
            return Task.CompletedTask;
        };

        if (connectedEvent != null)
            mqttClient.ConnectedAsync += connectedEvent;

        // MQTT Disconnected Events
        mqttClient.DisconnectedAsync += (e) =>
        {
            Console.WriteLine("Disconnected from MQTT Broker");
            return Task.CompletedTask;
        };

        if (disconnectedEvent != null)
            mqttClient.DisconnectedAsync += disconnectedEvent;

        // MQTT Message Received Events
        mqttClient.ApplicationMessageReceivedAsync += (e) =>
        {
            var action = TopicHandlers.GetValueOrDefault(e?.ApplicationMessage?.Topic ?? "");
            return action?.Invoke(e?.ApplicationMessage?.PayloadSegment.Array ?? []);
        };

        await mqttClient.ConnectAsync(clientOptions.ToMqttClientOptions());
        return new MQTTClientManager(mqttClient);
    }
    
    #endregion
    
    private MQTTClientManager(IMqttClient mqttClient)
    {
        _mqttClient = mqttClient;
    }

    private readonly IMqttClient _mqttClient;

    #region Async
    
    public Task<MQTTClientManager> PublishMessageAsync(
        string topic,
        string payload,
        MqttQualityOfServiceLevel qos = MqttQualityOfServiceLevel.AtLeastOnce,
        Encoding? encoding = null)
    {
        encoding ??= Encoding.UTF8;
        return PublishMessageAsync(topic, encoding.GetBytes(payload), qos);
    }

    public async Task<MQTTClientManager> PublishMessageAsync(
        string topic,
        byte[] payload,
        MqttQualityOfServiceLevel qos = MqttQualityOfServiceLevel.AtLeastOnce)
    {
        var message = new MqttApplicationMessage()
        {
            Topic = topic,
            PayloadSegment = payload,
            QualityOfServiceLevel = qos,
        };

        await _mqttClient.PublishAsync(message);
        return this;
    }

    public async Task<MQTTClientManager> SubscribeAsync(
        string topic,
        Func<byte[], Task> messageHandler,
        MqttQualityOfServiceLevel qos = MqttQualityOfServiceLevel.AtLeastOnce)
    {
        await _mqttClient.SubscribeAsync(new MqttTopicFilter()
        {
            Topic = topic,
            QualityOfServiceLevel = qos
        });

        TopicHandlers[topic] = messageHandler;
        return this;
    }

    public Task DisconnectAsync()
    {
        return _mqttClient.DisconnectAsync();
    }

    public MQTTClientManager SetDefaultEventHandler(Func<byte[], Task> messageHandler)
    {
        TopicHandlers[""] = messageHandler;
        return this;
    }
    
    #endregion
    
    #region Sync
    public void PublishMessage(string topic, string payload,
        MqttQualityOfServiceLevel qos = MqttQualityOfServiceLevel.AtLeastOnce, Encoding? encoding = null)
    {
        encoding ??= Encoding.UTF8;
        PublishMessage(topic, encoding.GetBytes(payload), qos);
    }

    public void PublishMessage(string topic, byte[] payload,
        MqttQualityOfServiceLevel qos = MqttQualityOfServiceLevel.AtLeastOnce)
    {
        var message = new MqttApplicationMessage()
        {
            Topic = topic,
            PayloadSegment = payload,
            QualityOfServiceLevel = qos,
        };

        _mqttClient.PublishAsync(message).Wait();
    }

    public void Subscribe(string topic, Action<byte[]> messageHandler,
        MqttQualityOfServiceLevel qos = MqttQualityOfServiceLevel.AtLeastOnce)
    {
        _mqttClient.SubscribeAsync(new MqttTopicFilter()
        {
            Topic = topic,
            QualityOfServiceLevel = qos
        }).Wait();

        TopicHandlers[topic] = (payload) =>
        {
            messageHandler(payload);
            return Task.CompletedTask;
        };
    }

    public void Disconnect()
    {
        _mqttClient.DisconnectAsync().Wait();
    }

    public MQTTClientManager SetDefaultEventHandler(Action<byte[]> messageHandler)
    {
        TopicHandlers[""] = (payload) =>
        {
            messageHandler(payload);
            return Task.CompletedTask;
        };
        return this;
    }
    #endregion
    
}